package cn.tiny.common.concurrent.lock;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * 本地锁：非公平锁实现
 * Created on 2019/5/4 10:29
 */
public class LocalLock implements Lock {
    private AtomicStampedReference<Thread> current = new AtomicStampedReference<>(null, 0);
    private LinkedBlockingQueue<Thread> waitingQueue = new LinkedBlockingQueue<>();

    /**
     * 获取锁：阻塞
     * Created on 2019/5/4 10:17
     */
    @Override
    public void lock() {
        /**
         * 1.获取到锁直接返回
         * 2.为获取到锁加入等待队列并释放线程等待
         * 3.获得锁线程解锁后把等待队列
         */
        while (!doLock()) {
            waitingQueue.add(Thread.currentThread());
            LockSupport.park();
            waitingQueue.remove(Thread.currentThread());
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        if(doLock() || current.getReference() == Thread.currentThread()) {
            return true;
        }
        return false;
    }

    /**
     * 获取锁：非阻塞
     * Created on 2019/5/4 10:28
     */
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        long nanosTimeout = unit.toNanos(time);
        if(nanosTimeout <= 0L){
            return false;
        }
        final long deadline = System.nanoTime() + nanosTimeout;
        for (;;) {
            if(doLock()){
                return true;
            }
            if(System.nanoTime() >= deadline)
                return false;
            if(doLock()){
                LockSupport.parkNanos(this, nanosTimeout);
            }
        }
    }

    private boolean doLock(){
        return current.compareAndSet(null, Thread.currentThread(), 0, 1);
    }

    @Override
    public void unlock() {
        /**
         * 1、释放锁
         * 2、唤醒等待中的线程
         */
        if(current.compareAndSet(Thread.currentThread(), null, 1, 0)){
            Object objects[] = waitingQueue.toArray();
            for (Object object : objects){
                LockSupport.unpark((Thread) object);
            }
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
